package com.cskaoyan.javase.oop2._2polymorphic._3exercise;

/**
 * 读程序题
 * 对于"对象名点成员方法"的调用,怎么去看它的调用结果呢?分三步走:
 *      1.先看引用的数据类型,因为它决定访问范围,如果是一个父类类型,那就无法访问子类独有方法,如果是一个子类引用,就必须同时关注父类 + 子类.
 *
 *      2.其次要关注对象的数据类型,尤其是"父类引用指向子类对象"
 *      因为方法调用的结果要根据具体对象的类型来决定
 *      如果子类中重写了父类的成员方法
 *      那么就需要注意"父类引用指向子类对象"调用重写的成员方法,其结果是子类中的行为
 *
 *      3.根据以上原则,判断后,完全有可能出现多个方法同时匹配的情况,因为方法可以重载
 *      那么这时,如何判断调用哪个方法呢?
 *      就近原则
 *      看形参和实参的数据类型的关系
 *      如果有恰好直接匹配的,要优先考虑直接匹配的
 *      如果没有,那就考虑类型转换次数少的
 *
 * @since 11:04
 * @author wuguidong@cskaoyan.onaliyun.com
 */

public class Demo2 {
    public static void main(String[] args) {
        A a1 = new A();
        A a2 = new B();
        B b = new B();
        C c = new C();
        D d = new D();
        // ----------------------------------
        /*
            a1这个引用是父类引用指向父类对象
            所以整个全程方法调用只需要看类A就可以了
         */
        System.out.println("1--" + a1.show(b)); // AA
        System.out.println("2--" + a1.show(c)); // AA
        System.out.println("3--" + a1.show(d)); // AD
        // ----------------------------------
        /*
            a2这个引用是父类引用指向子类对象
            所以a2的访问范围仍然是类A
            但是对象是子类对象
            所以和a1的区别是:
            如果某个方法在子类中重写了,而对象也是子类对象的话,在这种情况下,调用方法,体现子类的行为
         */
        System.out.println("4--" + a2.show(b)); // BA
        System.out.println("5--" + a2.show(c)); // BA
        System.out.println("6--" + a2.show(d)); // AD

        // ----------------------------------
        /*
            子类引用指向子类对象
            这时的引用访问范围是 子类 + 父类
         */
        System.out.println("7--" + b.show(b)); // BB
        System.out.println("8--" + b.show(c)); // BB
        System.out.println("9--" + b.show(d)); // AD
    }
}

class A {
    // 父类独有的方法
    public String show(D obj) {
        return ("A and D");
    }
    public String show(A obj) {
        return ("A and A");
    }
}

class B extends A {
    // 子类独有的方法
    public String show(B obj) {
        return ("B and B");
    }
    // 父子类中子类重写父类的方法
    @Override
    public String show(A obj) {
        return ("B and A");
    }
}

class C extends B {
}

class D extends B {
}